/*
 * ArbolBMas.cpp
 *
 *  Created on: Sep 21, 2011
 *  Author: Nodo Team
 */

#include "ArbolBMas.h"

ArbolBMas::ArbolBMas() {

	this->setCapacidadMaximaNodosInternos(0);
	this->setCapacidadMinimaNodosInternos(0);
	this->setCapacidadMaximaNodosHoja(0);
	this->setCapacidadMinimaNodosHoja(0);
	Nodo* nodo = new NodoHoja();
	this->setRaiz(nodo);
	this->ultimaHojaLeida = this->getRaiz(); //TODO ok?
}

void ArbolBMas::insertar(Registro* dato) {
	ResultadoOperacion resultado = this->insertarInterno(this->getRaiz(), dato);

	switch (resultado.getEstado()) {
	case OK:
		break;
	case OVERFLOW: {
		//agrego el nivel, hay q controlar lo del nivel cuando se crean nodos nuevos.
		Nodo* nuevoHijoIzquierdo = getNodoNuevo(this->getRaiz()->getNivel());
		Nodo* nuevoHijoDerecho = getNodoNuevo(this->getRaiz()->getNivel());

		//Incremento nivel de la raiz en 1.
		this->getRaiz()->setNivel(this->getRaiz()->getNivel() + 1);

		nuevoHijoIzquierdo->setListaRegistros(this->getRaiz()->getListaRegistrosIzq(resultado.getDato()->getClave()));
		nuevoHijoDerecho->setListaRegistros(this->getRaiz()->getListaRegistrosDer(resultado.getDato()->getClave()));

		vector<Registro*>* listaRegistrosRaiz = new vector<Registro*>();
		Registro* registroRaiz = new RegistroInterno(resultado.getDato()->getClave(),
				nuevoHijoIzquierdo->getNumeroNodo(),
				nuevoHijoDerecho->getNumeroNodo());

		listaRegistrosRaiz->push_back(registroRaiz);
		this->getRaiz()->setListaRegistros(listaRegistrosRaiz);

		this->guardarNodo(nuevoHijoIzquierdo);
		this->guardarNodo(nuevoHijoDerecho);
		this->guardarNodo(this->getRaiz());

		break;
	}
	case MODIFICADO:
		this->guardarNodo(this->getRaiz());
		break;
	default:
		break;
	}

}

ResultadoOperacion ArbolBMas::insertarInterno(Nodo* nodo, Registro* dato) {

	if (nodo->getNivel() == 0) {
		return nodo->insertar(dato);

	} else {
		int numeroNodo = ((NodoInterno*) nodo)->numeroNodoHijoALeer(
				dato->getClave());
		Nodo* nodoHijo = this->getNodo(numeroNodo);
		ResultadoOperacion resultado = insertarInterno(nodoHijo, dato);
		switch (resultado.getEstado()) {
		case OK:
			return resultado;
		case OVERFLOW: {
			//TODO ver cómo se declara vector<Registro*>
			vector<Registro*>* listaRegistrosNuevoNodo =
					new vector<Registro*>();
			listaRegistrosNuevoNodo->push_back(resultado.getDato());

			//Devuelve los registros de la mitad derecha del nodo hijo.
			//TODO: en getLIstaRegistrosDer ver como asignarle la mitad + 1 para una hoja y/o raiz
			vector<Registro*>* listaRegistrosDelHijo =
					nodoHijo->getListaRegistrosDer(resultado.getDato()->getClave());

			//Agrego los registros de la mitad derecha al padre
			for (unsigned int i = 0; i < listaRegistrosDelHijo->size(); i++) {
				listaRegistrosNuevoNodo->push_back(
						listaRegistrosDelHijo->at(i));
			}

			//Creo el nuevo nodo
			Nodo* nodoNuevo = this->getNodoNuevo(nodoHijo->getNivel());
			nodoNuevo->setListaRegistros(listaRegistrosNuevoNodo);
			setNodoNuevo(nodoNuevo, nodoHijo);

			Registro* registroInterno = new RegistroInterno(
					resultado.getDato()->getClave(), nodoHijo->getNumeroNodo(),
					nodoNuevo->getNumeroNodo());

			this->guardarNodo(nodoNuevo);
			this->guardarNodo(nodoHijo);

			return nodo->insertar(registroInterno);
		}
		case MODIFICADO: {
			this->guardarNodo(nodoHijo);
			resultado.setEstado(OK);
			return resultado;
			break;
		}
		default:
			//TODO: ver que retorna
			ResultadoOperacion* resultadoDefault = new ResultadoOperacion();
			return *resultadoDefault;
			break;
		}
	}
}

void ArbolBMas::guardarNodo(Nodo *nodo) {
}

void ArbolBMas::setNodoNuevo(Nodo* nodoNuevo, Nodo* nodoHijo) {

	if (nodoNuevo->getNivel() == 0) {
		NodoHoja* nodoHoja = (NodoHoja*) nodoNuevo;
		NodoHoja* nodoHojaHijo = (NodoHoja*) nodoHijo;

		nodoHoja->setReferenciaNodoSiguiente(
				nodoHojaHijo->getReferenciaNodoSiguiente());
		nodoHoja->setReferenciaNodoSiguiente(nodoNuevo->getNumeroNodo());
		nodoNuevo->setNivel(0);
		nodoNuevo->setCapacidadMin(this->getCapacidadMinimaNodosHoja());
		nodoNuevo->setCapacidadMax(this->getCapacidadMaximaNodosHoja());

	} else {

		nodoNuevo->setNivel(nodoHijo->getNivel());
		nodoNuevo->setCapacidadMin(this->getCapacidadMinimaNodosInternos());
		nodoNuevo->setCapacidadMax(this->getCapacidadMaximaNodosInternos());
	}
}

Nodo* ArbolBMas::getRaiz() const {
	return this->raiz;
}

Nodo* ArbolBMas::getUltimaHojaLeida() const {
	return this->ultimaHojaLeida;
}

void ArbolBMas::setRaiz(Nodo *raiz) {
	this->raiz = raiz;
}

void ArbolBMas::setUltimaHojaLeida(Nodo *ultimaHojaLeida) {
	this->ultimaHojaLeida = ultimaHojaLeida;
}

Nodo* ArbolBMas::getNodo(int numero) {
	return NULL; //manjeador archivo nodos -> getNodo(numero);
}

Nodo* ArbolBMas::getNodoNuevo(int nivelNodoHijo) {

	int numeroNodoACrear = 0;
	//if (manjeador archivo nodos libres-> tenesNodo())TODO este metodo debería decirme si tiene nodos
	//TODO return manjeador archivo nodos libres-> getNodoLibre();
	//numeroNodoACrear = nodos libres-> getNodoLibre()->getNumero;
	//else
	//numeroNodoACrear = archivos nodoss-> getNodoNuevo()->getNumero;
	//		return manjeador archivo nodos -> getNodoNuevo();

	//TODO:antes del return en cada caso evalua que tipo de nodo instanciar
	// etaria bueno que cada manejador me devuelva el numero de nodo y no el nodo asi los creo yo
	if (nivelNodoHijo == 0) {
		Nodo* nodoHoja = new NodoHoja();
		nodoHoja->setNumeroNodo(numeroNodoACrear);
		return nodoHoja;
	} else {
		Nodo* nodoInterno = new NodoInterno();
		nodoInterno->setNumeroNodo(numeroNodoACrear);
		return nodoInterno;
	}

}

ArbolBMas::~ArbolBMas() {
	// close Manejador Archivo de Nodos
	// close Manejador Archivo de Nodos Libres
	this->getRaiz()->~Nodo(); //TODO ok? es la única referencia que tenemos a nodo, no?
	this->ultimaHojaLeida->~Nodo(); //TODO ok?

}

int ArbolBMas::getCapacidadMaximaNodosHoja() const {
	return capacidadMaximaNodosHoja;
}

int ArbolBMas::getCapacidadMaximaNodosInternos() const {
	return capacidadMaximaNodosInternos;
}

int ArbolBMas::getCapacidadMinimaNodosHoja() const {
	return capacidadMinimaNodosHoja;
}

int ArbolBMas::getCapacidadMinimaNodosInternos() const {
	return capacidadMinimaNodosInternos;
}

void ArbolBMas::setCapacidadMaximaNodosHoja(int capacidadMaximaNodosHoja) {
	this->capacidadMaximaNodosHoja = capacidadMaximaNodosHoja;
}

void ArbolBMas::setCapacidadMaximaNodosInternos(
		int capacidadMaximaNodosInternos) {
	this->capacidadMaximaNodosInternos = capacidadMaximaNodosInternos;
}

void ArbolBMas::setCapacidadMinimaNodosHoja(int capacidadMinimaNodosHoja) {
	this->capacidadMinimaNodosHoja = capacidadMinimaNodosHoja;
}

void ArbolBMas::setCapacidadMinimaNodosInternos(
		int capacidadMinimaNodosInternos) {
	this->capacidadMinimaNodosInternos = capacidadMinimaNodosInternos;
}

